package cn.com.fone.modules.api.common.split;

import cn.com.fone.modules.api.common.split.annotation.SplitTable;
import cn.com.fone.modules.api.common.split.strategy.Strategy;
import cn.com.fone.modules.api.common.split.strategy.StrategyManager;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 实现分表拦截器
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class SplitInterceptor implements Interceptor {


    private static final Logger LOGGER = LoggerFactory.getLogger(SplitInterceptor.class);


    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();

    @Override
    public Object plugin(Object o) {
        //  当目标类是StatementHandler类型时，才包装目标类，否者直接返回目标本身,减少目标被代理的次数
        if (o instanceof StatementHandler) {
            return Plugin.wrap(o, this);
        } else {
            return o;
        }
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        LOGGER.info("执行分表拦截器...");
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaStatementHandler = MetaObject.forObject(
                statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);
        // 传递给下一个拦截器处理
        Object parameterObject =  metaStatementHandler.getValue("delegate.boundSql.parameterObject");
        doSplitTable(metaStatementHandler, parameterObject);
        return invocation.proceed();
    }


    @Override
    public void setProperties(Properties properties) {
    }


    private void doSplitTable(MetaObject metaStatementHandler, Object param) throws Exception {
        String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
        if (StringUtils.isNotBlank(originalSql)) {
            LOGGER.info("分表前的SQL:{}", originalSql);
            MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
            // 通过反射获取类信息
            String id = mappedStatement.getId();
            // 类名
            String className = id.substring(0, id.lastIndexOf("."));
            // 方法名
            String methodName = id.substring(id.lastIndexOf(".") + 1);
            Class<?> clazz = Class.forName(className);
            Method method = findMethod(clazz.getDeclaredMethods(), methodName);
            // 根据配置自动生成分表SQL
            SplitTable tableSplit = null;
            // 获取方法注解
            if (method != null) {
                tableSplit = method.getAnnotation(SplitTable.class);
            }

            // 获取类注解
            if (tableSplit == null) {
                tableSplit = clazz.getAnnotation(SplitTable.class);
            }

            if (tableSplit != null
                    && tableSplit.split()
                    && StringUtils.isNotBlank(tableSplit.strategy().toString())) {
                StrategyManager strategyManager = ContextHelpers.getBean(StrategyManager.class);

                // 初始化策略类
                if (strategyManager.strategies.isEmpty()) {
                    strategyManager.initStrategy();
                }

                String convertedSql = "";
                String[] strategies = tableSplit.strategy().toString().split(",");
                // 获取参数
                ParameterMap paramMap = mappedStatement.getParameterMap();
                for (String str : strategies) {
                    Strategy strategy = strategyManager.getStrategy(str);
                    Map<String, Object> params = new HashMap<String, Object>();
                    params.put(Strategy.TABLE_NAME, tableSplit.value());
                    params.put(Strategy.SPLIT_FIELD, tableSplit.field());
                    params.put(Strategy.EXECUTE_PARAM_DECLARE, paramMap);
                    params.put(Strategy.STRATEGY, tableSplit.strategy().toString());
                    params.put(Strategy.EXECUTE_PARAM_VALUES, param);
                    convertedSql = originalSql.replaceAll(tableSplit.value(), strategy.splitTable(params));
                }
                metaStatementHandler.setValue("delegate.boundSql.sql", convertedSql);
                LOGGER.info("分表后的SQL:{}", convertedSql);
            }
        } else {
            LOGGER.error("获取分表前sql为空，originalSql=" + originalSql);
        }
    }

    private Method findMethod(Method[] methods, String methodName) {
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                return method;
            }
        }
        return null;
    }

}
